#ifndef __SERVER_H
#define __SERVER_H

#define XNOVERBOSE

void closeserver();
void destroy_tries(); 
void sigint_handler(int sig);

nPatriciaTrie<nPatriciaTrie<char>* >* File;
nPatriciaTrie<nPatriciaTrie<char>* >* Tags;
char customindex[500]="\0";
char *commonheadkey;
bool removetag(char *filename,char *tag);
bool addtag(char *filename,char *tag); 
bool readfromfile();
bool writetofile();
bool removefile(char *whichfile);
bool copyfile(char *src,char *dest); 
bool movefile(char *src,char *dest); 
linkedlist* listfiles(char *withtag);
linkedlist* listNfilessingletag(char *withtag,bool first);
linkedlist* listNfiles(linkedlist *withtags,bool first);
linkedlist* listtags(char *offile);
linkedlist* suggest(linkedlist* files);
linkedlist* search(linkedlist *withtags);
linkedlist* slashlist();
void init();
char lastguy[300]; // Used for listNfiles
void init() {
	commonheadkey=NULL;
}

bool readfromfile() {

	FILE *fp;
	char str[100000];
	char *tags;
	char *fil;
	File = new nPatriciaTrie< nPatriciaTrie<char>*>();
	Tags = new nPatriciaTrie< nPatriciaTrie<char>*>();

	printf("file read : %s \n",customindex);
	fp=fopen(customindex,"r");

	if (fp==NULL)
		return false;
	while(!feof(fp)) {
		fgets(str,10000,fp);
		if (feof(fp))
			break;
		fil  = strtok(str,",");
		tags = strtok(NULL,",");
		while (tags!=NULL) { 
	                addtag(fil,tags);
			tags=strtok(NULL,",\n");
		}
		
	}
	fclose(fp);
	return true;
}

bool writetofile() {
	
	FILE *fp;
	linkedlist *l,*t;
	char *st;

	printf("file write : %s \n",customindex);
	fp=fopen(customindex,"w+");

	if (fp==NULL)
		return false;
	l=File->getList();

//MBM:Testing saving to disk	File->saveToFile();

	char *name;
	while (!(l->isEmpty())) {
		name=l->remove();
		fprintf(fp,"%s,",name);
  		nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
		temp=File->LookupNode(name);
		t=(temp->GetData())->getList();
		while (!(t->isEmpty())) {
			st=t->remove();
			fprintf(fp,"%s,",st);
			free(st);
		}
		fprintf(fp,"\n");
	}

	if(l!=NULL)
	{
		l->removeAll();
		delete l;
	}

	fclose(fp);
	return true;
}

bool removetag(char *filename,char *tag) {

	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
	nPatriciaTrie<char> *tgtemp;
	char *filep,*tagp,*st;
	bool deletefile,deletetag;
	deletefile=false;
	deletetag=false;	
	filep=NULL;
	tagp=NULL;

	// Modify the File Trie

	temp=File->LookupNode(filename);

	#ifndef XNOVERBOSE
	if (temp)
		printf("%s is tagged\n",filename);
	else
		printf("%s is not tagged\n",filename);
	#endif

	if (temp==NULL) {
		#ifndef XNOVERBOSE
		printf("%s File not at all tagged \n",filename);
		#endif
		return false;
	}
	filep=temp->GetKey();
	tgtemp=(nPatriciaTrie<char>*)(temp->GetData())->LookupNode(tag);
	if (tgtemp==NULL) {
		#ifndef XNOVERBOSE
		printf("%s File does not contain tag %s\n",filename,tag);
		#endif
		return false;
	}
 
	// Delete tag from the File structure	
	if(!(nPatriciaTrie<char>*)(temp->GetData())->Delete(tag)) {
		printf("ERROR!!! deleting tag %s from file %s\n",tag,filename);
		return false;
 	} else {
		#ifndef XNOVERBOSE	
		printf("Tag %s deleted from %s \n",tag,filename);
		#endif
	}

	// If it was the last tag, then remove the file
	if ((nPatriciaTrie<char>*)(temp->GetData())->isEmpty()) { 
		// Destroy the second level trie associated
		delete temp->GetData();

		if(!(File->Delete(filename))) { 
			printf("ERROR!!! Last tag %s from %s removal\n",tag,filename);
			return false;
		}
		else {
			deletefile=true;
			#ifndef XNOVERBOSE
			printf("Last tag %s from %s removed\n",tag,filename);
			#endif
		}
		
	}
	
	// Modify the Tag Trie

	temp=Tags->LookupNode(tag);

	#ifndef XNOVERBOSE
	if (temp)
		printf("%s is tag is present\n",tag);
	else
		printf("%s is tag is not present\n",tag);
	#endif

	if (temp==NULL) {
		#ifndef XNOVERBOSE
		printf("%s tag not present \n",tag);
		#endif
		return false;
	}
	tagp=temp->GetKey();
	tgtemp=(nPatriciaTrie<char>*)(temp->GetData())->LookupNode(filename);
	if (tgtemp==NULL) {
		#ifndef XNOVERBOSE
		printf("%s Tag not found in File %s\n",tag,filename);
		#endif
		return false;
	}
 
	// Delete tag from the Tag structure	
	if(!(nPatriciaTrie<char>*)(temp->GetData())->Delete(filename)) {
		printf("ERROR!!! deleting file %s from tag %s\n",filename,tag);
		return false;
 	} else {
		#ifndef XNOVERBOSE	
		printf("file %s deleted from tag %s \n",filename,tag);
		#endif
	}

	// If it was the last file, then remove the tag
	if ((nPatriciaTrie<char>*)(temp->GetData())->isEmpty()) { 
		// Destroy the second level trie associated
		delete temp->GetData();
		
		if(!(Tags->Delete(tag))) { 
			printf("ERROR!!! Last file %s from tag %s removal\n",filename,tag);
			return false;
		}
		else {
			deletetag=true;
			#ifndef XNOVERBOSE
			printf("Last file %s from tag %s removed\n",filename,tag);
			#endif
		}
		
	}
	if (deletefile) {
		printf("Removing node location for : %s\n",filep);
		free(filep);
	}
	if (deletetag) {
		printf("Removing node location for : %s\n",tagp);
		free(tagp);
	}
	return true;
}

bool addtag(char *filenam,char *ta) { 
	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
	nPatriciaTrie<char> *tgtemp;
	char *filename,*tag;
	
	// Assign New strings is required
	
	temp=File->LookupNode(filenam);
	if (temp==NULL) {
	//	printf("Creating node location for : %s\n",filenam);
		filename=strdup(filenam);
	} else
		filename=temp->GetKey();
	if (filename==NULL) {
		printf("Cannot Allocate space for :--%s--\n",filenam);
		return false;
	}

	temp=Tags->LookupNode(ta);
	if (temp==NULL) {
	//	printf("Creating node location for : %s\n",ta);
		tag=strdup(ta);
	} else
		tag=temp->GetKey();
	
	// Modify the File Trie
	temp=File->LookupNode(filenam);
	#ifndef XNOVERBOSE
	if (temp) 
		printf("%s already tagged \n",filename);
	else
		printf("%s not tagged, adding to file list \n",filename);
			
	#endif

        // If the file is not present then add it to tagged files list	
	if (temp==NULL) {
	        tgtemp=new nPatriciaTrie<char>();
		temp=File->Insert(filename,tgtemp);
		if (!temp) {
			printf("Cannot add new file!!!\n");
			return false;
		}
	}

	// temp has pointer to our node
	
	// if it is a new file (no previous tags)
	if (temp->GetData()==NULL) {
	    tgtemp=new nPatriciaTrie<char>();
	    temp->SetData(tgtemp);
	}
	// *temp has a pointer to a Trie Structure
	tgtemp=(nPatriciaTrie<char>*)(temp->GetData())->LookupNode(tag);
	
	#ifndef XNOVERBOSE
	if (tgtemp) 
		printf("%s already tagged with \"%s\" \n",filename,tag);
	else
		printf("%s not tagged with \"%s\", adding tag \n",filename,tag);
	#endif
	if (tgtemp) 
		return false;	
	if (!tgtemp) 		
	        tgtemp=(nPatriciaTrie<char>*)(temp->GetData())->Insert(tag,'i');
	if (!tgtemp) {
		printf("Could not add tag \"%s\" to %s",tag,filename);
		return false;
	}

	// Modify the Tag Trie
		
	temp=Tags->LookupNode(tag);
	#ifndef XNOVERBOSE
	if (temp) 
		printf("%s tag already exists \n",tag);
	else
		printf("%s tag not found, adding to tag list \n",tag);
			
	#endif

        // If the tag is not present then add it to the tags list	
	if (temp==NULL) { 
	        tgtemp=new nPatriciaTrie<char>();
		temp=Tags->Insert(tag,tgtemp);
		if (!temp) {
			printf("Cannot add new tag for some reason\n");
			return false;
		}
	}

	// temp has pointer to our node
	
	// if it is a new tag (no previous files)
	if (temp->GetData()==NULL) {
	    tgtemp=new nPatriciaTrie<char>();
	    temp->SetData(tgtemp);
	}
	// *temp has a pointer to a Trie Structure
	tgtemp=(nPatriciaTrie<char>*)(temp->GetData())->LookupNode(filename);
	
	#ifndef XNOVERBOSE
	if (tgtemp) 
		printf("%s tag has file \n",tag,filename);
	else
		printf("%s tag doesnt have \"%s\", adding file to tags \n",tag,filename);
	#endif
	if (tgtemp) 
		return false;	
	if (!tgtemp) 		
	        tgtemp=(nPatriciaTrie<char>*)(temp->GetData())->Insert(filename,'i');
	if (!tgtemp) {
		printf("Could not add tag \"%s\" to %s",tag,filename);
		return false;
	}
	
	return true;
}	

bool removefile(char *whichfile) {
	linkedlist *t;
  	char *tm;
	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
	temp=File->LookupNode(whichfile);
	if (temp==NULL)
		return false;
	t=(temp->GetData())->getList();
	while (!(t->isEmpty())) {
		tm=t->remove();
		removetag(whichfile,tm);
		free(tm);
	}

	if(t!=NULL)
	{
		t->removeAll();
		delete t;
	}

	return true;
}
bool copyfile(char *src,char *dest) {
	if (strcmp(src,dest)==0)
		return false;
	linkedlist *t;
	char *tm;
  	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
	
	// Remove tags if dest already exists
	temp=File->LookupNode(dest);
	if (temp!=NULL)
		removefile(dest);
		
	temp=File->LookupNode(src);

	if (temp==NULL)
		return false;
	t=(temp->GetData())->getList();
	while (!(t->isEmpty())) {
		tm=t->remove();
		addtag(dest,tm);
		free(tm);
	}

	if(t!=NULL)
	{
		t->removeAll();
		delete t;
	}

	return true;
}

bool movefile(char *src,char *dest) {
	if (!(copyfile(src,dest)))
		return false;
	removefile(src);
	return true;
}

linkedlist* listfiles(char *withtag) {
	linkedlist *t;
	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
	if(withtag!=NULL) temp=Tags->LookupNode(withtag);
	if (temp!=NULL)
		t=(temp->GetData())->getList();
	else 
		t=NULL;
	return t;
}

linkedlist* listNfilessingletag(char *withtag,bool first) {
	linkedlist *t;
  	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
	temp=Tags->LookupNode(withtag);
	if (temp!=NULL)
		t=(temp->GetData())->getNitems(DTP,first);
	else 
		t=NULL;
	return t;
}

linkedlist* listNfiles(linkedlist *withtags,bool first) {

	linkedlist *t,*tmp,*out;
	char templist[10][300];
	t=NULL;
	int count=0,whichtag;
	bool exitme=false;
	bool jumpafter=true;

  	if (withtags->count()==1)  
		t=listNfilessingletag(withtags->remove(),first);
	else 
	{
		t = new linkedlist();
		if (first) 
			strcpy(lastguy,"");
		//printf("%s : %s \n",first?"TRUE":"FALSE",lastguy);
		while ((count<DTP) && (!exitme)) {
			nPatriciaTrie<char> *keyguy;
			keyguy = new nPatriciaTrie<char>();
			tmp=withtags->copyList();
			whichtag=0;

			// Step 1 : Find the largest in each list which is just greater than "lastguy"
			while(!(tmp->isEmpty()) && (!exitme)) {
				whichtag++;
				if (strlen(lastguy)==0)
					strcpy(templist[whichtag],"");
				nPatriciaTrieNode <char> *templevel;
				nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
				temp =	Tags->LookupNode(tmp->remove());
				if (temp==NULL)
					exitme=true;
				else {
//					printf("&&&& %s : %s : %s : %s\n",temp->GetKey(),jumpafter?"TRUE":"FALSE",lastguy,templist[whichtag]);
					if (jumpafter) {

						templevel = temp->GetData()->LookupNextNode((nPatriciaTrieKey)lastguy);


						if (templevel==NULL)
							exitme=true;
						else {
							strcpy(templist[whichtag],templevel->GetKey());
							keyguy->Insert(templevel->GetKey(),'c');
			//				printf("$$$$ %s \n",templevel->GetKey());
						}
					} else {
							
						templevel = temp->GetData()->LookupNode((nPatriciaTrieKey)lastguy);
						if (templevel==NULL)
							templevel = temp->GetData()->LookupNextNode((nPatriciaTrieKey)lastguy);

						if (templevel==NULL)
							exitme=true;
						else {
							strcpy(templist[whichtag],templevel->GetKey());
							keyguy->Insert(templevel->GetKey(),'c');
			//				printf("$$$$ %s \n",templevel->GetKey());
						}
					}
				}
			}
//		 	delete tmp;	
			if (!exitme) {
				// Step 2 : If all of them are equal then add to list, set lastguy
			//	printf("---- %s : ",keyguy->getList()->toString());
				linkedlist *tmp1;
				tmp1 = keyguy->getList(); 
				if (tmp1->count()==1) {
					count++;
					strcpy(lastguy,tmp1->remove());
					t->add(lastguy);
					jumpafter=true;
				} else {
					// Step 3 : else Set Lastguy = Largest of all the filenames obtained
					while (!tmp1->isEmpty())
	 					strcpy(lastguy,tmp1->remove());
					jumpafter=false;
				}
			//	delete tmp1;
			//	printf("%s $$$$(%s)$$$$\n",lastguy,jumpafter?"TRUE":"FALSE");
			}
		}
	}
	return t;
}

linkedlist* listtags(char *offile) {
			linkedlist *t;
		  	nPatriciaTrieNode<nPatriciaTrie<char>* >* temp;
			temp=File->LookupNode(offile);
			if (temp!=NULL)
				t=(temp->GetData())->getList();
			else 
				t=NULL;
			return t;
}

char* randomFile() {
		char *k;
		k=File->GetRandomKey();
		return strdup(k);
}

char* randomTag() {
		char *k;
		k=Tags->GetRandomKey();
		return strdup(k);
}

linkedlist* search(linkedlist *withtags) {
	linkedlist *fileslst,*tagslst;
	linkedlist *newfileslst;
	tagslst=withtags->copyList();

	fileslst=listfiles(tagslst->remove());
	if (fileslst==NULL)
	    fileslst=new linkedlist();
	
	while ((!(fileslst->isEmpty() )) && (!(tagslst->isEmpty()))) {
	    newfileslst=listfiles(tagslst->remove());
	    if ((newfileslst==NULL)||(newfileslst->isEmpty()))
		fileslst->removeAll();
	    else 
		fileslst->selfintersect(newfileslst);
	}
	if (newfileslst!=NULL)
		newfileslst->removeAll();
	if (tagslst!=NULL)
		tagslst->removeAll();
	return fileslst;
}

linkedlist* suggest(linkedlist* files){
	nPatriciaTrie<char> *sug;
	sug = new nPatriciaTrie<char>();
	char *tm;	
	linkedlist *lst,*tmpfiles;
	if (files==NULL)
		return NULL;
	tmpfiles=files->copyList();
	while (!(tmpfiles->isEmpty())) {
		tm=tmpfiles->remove();
	        lst=listtags(tm);
		free(tm);
		while ((lst!=NULL) && !(lst->isEmpty())) {
			tm=lst->remove();
			sug->Insert(tm,'\0');
			free(tm);
		}
	    	if (lst!=NULL)
			delete lst;
	}
	lst=sug->getList();
	sug->deleteMemory();
	return lst;
}

linkedlist* slashlist() {
	// Return all the directory tags!!!!
	linkedlist* tmp,*old;
	char *temp;
	tmp=Tags->getPartialList((char*) "/");
 	
	/*
	 old=Tags->getList();
	tmp=new linkedlist();
	while (!old->isEmpty()) {
		temp=old->remove();
		if (beginsWith(temp,"/"))
			tmp->add(temp);
	}
	*/
	return tmp;
}

#endif
